﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hpdi.VssLogicalLib;

namespace Hpdi.VssDump
{
    class FSDumper
    {
        private System.IO.TextWriter output;
        private Dictionary<string, ItemInfo> itemMap;
        private List<RevisionInfo> revisions;
        private int itemCounter;
        private int revCounter;

        class ItemInfo
        {
            public VssItemName itemName;
            public string path;
            public int id;
            public ItemInfo(VssItemName itemName, string path, int id)
            {
                this.itemName = itemName;
                this.path = path;
                this.id = id;
            }
        }

        class RevisionInfo
        {
            public VssRevision revision;
            public ItemInfo item;
            public int id;
            public ItemInfo targetItem;
            public string oldName;
            public RevisionInfo(VssRevision revision, ItemInfo item, ItemInfo targetItem, string oldName, int id)
            {
                this.revision = revision;
                this.item = item;
                this.targetItem = targetItem;
                this.oldName = oldName;
                this.id = id;
            }
        }

        public FSDumper(System.IO.TextWriter output)
        {
            itemMap = new Dictionary<string, ItemInfo>();
            revisions = new List<RevisionInfo>();
            itemCounter = 1;
            revCounter = 1;
            this.output = output;
        }

        private void ProcessProject(VssProject project)
        {
            Console.Error.WriteLine(project.Path);
            ItemInfo itemInfo;
            if (!itemMap.TryGetValue(project.Path, out itemInfo))
            {
                itemInfo = new ItemInfo(project.ItemName, project.Path, itemCounter++);
                itemMap.Add(project.Path, itemInfo);
            }
            foreach (var rev in project.Revisions)
            {
                string targetItemPath = rev.Project.Path;
                VssItemName targetItem = null;
                string oldName = null;
                switch (rev.Action.Type)
                {
                    case VssActionType.Destroy:
                    case VssActionType.Delete:
                    case VssActionType.Add:
                    case VssActionType.Branch:
                    case VssActionType.Share:
                    case VssActionType.Recover:
                    case VssActionType.Rename:
                        targetItem = ((VssNamedAction)rev.Action).Name;
                        targetItemPath += "/" + ((VssNamedAction)rev.Action).Name.LogicalName;
                        break;
                }
                if (rev.Action.Type == VssActionType.Rename)
                    oldName = ((VssRenameAction)rev.Action).OriginalName;

                RevisionInfo newRev;
                if (rev.Action is VssNamedAction)
                {
                    ItemInfo targetItemInfo;
                    if (!itemMap.TryGetValue(targetItemPath, out targetItemInfo))
                    {
                        targetItemInfo = new ItemInfo(targetItem, targetItemPath, itemCounter++);
                        itemMap.Add(targetItemPath, targetItemInfo);
                    }
                    newRev = new RevisionInfo(rev, itemInfo, targetItemInfo, oldName, revCounter++);
                }
                else
                {
                    newRev = new RevisionInfo(rev, itemInfo, null, null, revCounter++);
                }
                revisions.Add(newRev);
            }
            foreach (var file in project.Files)
            {
                string path = file.GetPath(project);
                if (!itemMap.TryGetValue(path, out itemInfo))
                {
                    itemInfo = new ItemInfo(file.ItemName, file.GetPath(project), itemCounter++);
                    itemMap.Add(path, itemInfo);
                }
                foreach (var rev in file.Revisions)
                {
                    RevisionInfo newRev = new RevisionInfo(rev, itemInfo, null, null, revCounter++);
                    revisions.Add(newRev);
                }
            }
            foreach (var subProject in project.Projects)
            {
                ProcessProject(subProject);
            }
        }

        public void DumpProject(VssProject project)
        {
            ProcessProject(project);
            output.WriteLine("if object_id('FileRev') is not null drop table FileRev");
            output.WriteLine("if object_id('FilePath') is not null drop table FilePath");
            output.WriteLine("CREATE TABLE FilePath(Path nvarchar(max) not null, IsProject bit not null, FilePathKey int not null primary key)");
            output.WriteLine("CREATE TABLE FileRev(Revision int not null, [User] nvarchar(64) not null, CommitTime datetime, Action nvarchar(8) not null, " +
                "Label nvarchar(64) null, Comment nvarchar(max) null, FilePathKey int not null, OldName nvarchar(max) null, TargetFilePathKey int null, " +
                "FileRevKey int not null primary key, " +
                "CONSTRAINT FK_FileRev_FilePath FOREIGN KEY (FilePathKey) REFERENCES FilePath(FilePathKey))");
            output.WriteLine("SET NOCOUNT ON");
            output.WriteLine("GO");

            int batchCounter = 1;
            foreach (var itemEntry in itemMap)
            {
                output.WriteLine("INSERT INTO FilePath(Path, IsProject, FilePathKey) VALUES('{0}', {1}, {2})",
                    itemEntry.Value.path.Replace("'", "''"), (itemEntry.Value.itemName.IsProject ? "1" : "0"), itemEntry.Value.id);
                if (batchCounter++ % 100 == 0)
                    output.WriteLine("GO");
            }
            foreach (var rev in revisions)
            {
                output.WriteLine("INSERT INTO FileRev(Revision, [User], CommitTime, Action, Label, Comment, FilePathKey, OldName, TargetFilePathKey, FileRevKey) " +
                    "VALUES({0},'{1}','{2}','{3}',{4},{5},{6},{7},{8},{9})",
                    rev.revision.Version, rev.revision.User, rev.revision.DateTime.ToString("s"), rev.revision.Action.Type,
                    (string.IsNullOrEmpty(rev.revision.Label) ? "NULL" : "'" + rev.revision.Label.Replace("'", "''") + "'"),
                    (string.IsNullOrEmpty(rev.revision.Comment) ? "NULL" : "'" + rev.revision.Comment.Replace("'", "''") + "'"),
                    rev.item.id, (string.IsNullOrEmpty(rev.oldName) ? "NULL" : "'" + rev.oldName.Replace("'", "''") + "'"),
                    (rev.targetItem == null ? "NULL" : rev.targetItem.id.ToString()), rev.id);
                if (batchCounter++ % 100 == 0)
                    output.WriteLine("GO");
            }
        }
    }
}
